<?php
// +----------------------------------------------------------------------------
// | Tke团队版权所属 [三十年河东三十年河西,莫欺少年穷.!]
// +----------------------------------------------------------------------------
// | Copyright (c) 2014 http://www.itdaodan.com All rights reserved.
// +----------------------------------------------------------------------------
// | Author: PHP@妖孽 <dino_ma@163.com>
// +----------------------------------------------------------------------------
/**
 * @todo 支付网关对外接口
 * @author PHP@妖孽 <dino_ma@163.com>
 * @since 2015-04-01
 */
namespace Api\Controller;
use Think\Controller;
class PayController extends Controller {
	
	private  $auth_id;//来源Id
	private  $auth_key;//来源密钥
	private  $key;//哈希值
	private  $handel;//句柄
	
	/**
	 * @todo 初始化网关
	 */
	public function _initialize(){
		$this->checkAuth();
	}
	
	/**
	 * 操作错误跳转的快捷方法
	 * @access protected
	 * @param string $message 错误信息
	 * @param string $jumpUrl 页面跳转地址
	 * @param mixed $ajax 是否为Ajax方式 当数字时指定跳转时间
	 * @return void
	 */
	protected function error($message='',$jumpUrl='',$ajax=true) {
	    parent::error($message,$jumpUrl,$ajax);
	}
	
	/**
	 * @todo 接口验证
	 * $data_from = array(
	 * 	'para'=>array(
	 * 		'action'=>'test',
	 * 		'request_key'=>'test',
	 * 		'request_title'=>'test',
	 * 		'key'=>'test',//url编码并生成query字符串  之后sha1加密.
	 * 	);
	 * );
	 * @Author: PHP@妖孽 <dino_ma@163.com>
	 */
	private  function checkAuth(){
		//验证请求源 只支持白名单的阿里云内网 IP.
		//验证白名单
// 		if(!in_array(get_client_ip(), C('ALLOW_PAY_CLIENT'))){
// 			\Think\Log::write('数据请求包为空.client IP:'.get_client_ip());
// 			$this->error('不要恶意请求哦');
// 		}
		//验证请求次数
		$data_from = trim(I('para',''));
		if($data_from == ''){
			\Think\Log::write('数据请求包为空.client IP:'.get_client_ip());
			$this->error('哥们没有数据你请求啥阿?');
		}
// 		import('Common.Msj.Aes','','.php');
// 		$this->handel	=	new \Aes();
// 		$data_from = json_decode ( urldecode ( $this->handel->jjhhAesDecode ( str_replace ( " ", "+", $data_from ) ) ), true );
		$data_from = json_decode(\Think\Crypt::decrypt($data_from, C('PAY_SECRET_KEY')),true);
		if($data_from['auth_id']=='' || $data_from['auth_key']=='' || $data_from['key']=='' ||!array_key_exists('auth_id', $data_from) || !array_key_exists('auth_key', $data_from) || !array_key_exists('key', $data_from)){
			\Think\Log::write('缺少必备的请求参数.client IP:'.get_client_ip().'请求失败:auth_id:'.$data_from['auth_id'].'===auth_key:'.$data_from['auth_key'].'====key:'.$data_from['key']);
			$this->error('缺少请求必备参数!别TM在这逗我!');
		}
		
		$check_data_from = $data_from;
		unset($check_data_from['key']);
		if($data_from['key'] != data_auth_sign($check_data_from)){
			\Think\Log::write('client IP:'.get_client_ip().'请求失败,数据签名有问题');
			$this->error('key参数值有问题!别TM在这逗我!');
		}
		
		$this->auth_id		=	(int)trim($data_from['auth_id']);
		if($this->auth_id ==0){
			\Think\Log::write('client IP:'.get_client_ip().'请求失败,auth_id标识有问题');
			$this->error('auth_id参数值有问题!别TM在这逗我!');
		}
		
		$this->auth_key		=	trim($data_from['auth_key']);
		$auth_info = C('ALLOW_PAY_CLIENT_AUTH');
		if(!array_key_exists($this->auth_id, $auth_info)){
			//需要发短信,邮件去报警
			self::tellManager('000001');
			\Think\Log::write('client IP:'.get_client_ip().'请求失败,auth_id标识不存在,此次为恶意请求');
			$this->error('auth_id参数值不存在!别TM在这逗我!');
		}else{
			if($auth_info[$this->auth_id]!=$this->auth_key){
				//需要发短信,邮件去报警
				self::tellManager('000002');
				\Think\Log::write('client IP:'.get_client_ip().'请求失败,设备标识:'.$this->auth_id.'===设备密钥:'.$this->auth_key);
				$this->error('密钥有误!别TM在这逗我!');
			}
		}
		
		if (array_key_exists('data', $check_data_from)) {
			//验证请求数据,是否符合规则
			if(!is_array($check_data_from['data']) || empty($check_data_from['data'])){
				//需要发短信,邮件去报警
				self::tellManager('000003');
				\Think\Log::write('client IP:'.get_client_ip().'数据不合法'.arr2str($check_data_from['data'],','));
				$this->error('哥们没有数据你请求啥阿?乱搞!');
			}
			$check_data_from['data']['auth_id'] = $this->auth_id;
		}else{
			$check_data_from['data'] = array();
		}
		//验证请求动作
		if($check_data_from['action'] ==''){
			//需要发短信,邮件去报警
			self::tellManager('000006');
			\Think\Log::write('动作为空.client IP:'.get_client_ip().'请求失败');
			$this->error('动作不能为空哦 .~');
		}
		if(!method_exists($this, $check_data_from['action'])){
			//需要发短信,邮件去报警
			self::tellManager('000007');
			\Think\Log::write('动作不符合法.client IP:'.get_client_ip().'请求失败:动作:'.$check_data_from['action']);
			$this->error('弟弟.~你是在逗我玩么?你以为我会轻易的让你突破么?');
		}
		\Think\Log::write('有人设备请求了.client IP:'.get_client_ip().'请求成功'.'data:'.json_encode($check_data_from['data']));
		if(method_exists($this,$check_data_from['action'])){
		    $func = $check_data_from['action'];
			$this->$func($check_data_from['data']);
		}else{
			$this->error('请不要逗逼');
		}
// 		call_user_method($check_data_from['action'], $this,$check_data_from['data']);
	}
	
	/**
	 * @todo 支付动作
	 * @param array $data
	 */
	private function pay(&$data){
		//验证必要参数
		if(!array_key_exists('pay_type', $data)){
			$this->error('缺少支付方式~');
		}
		if(!array_key_exists('goods_title', $data)){
			$this->error('缺少goods_title请求参数');
		}
		if(!array_key_exists('goods_des', $data)){
			$this->error('缺少goods_des请求参数');
		}
		if(!array_key_exists('order_money', $data)){
			$this->error('缺少order_money请求参数');
		}
		if(!array_key_exists('pay_type', $data)){
			$this->error('缺少pay_type请求参数');
		}
		if(!array_key_exists('set_url', $data)){
			$this->error('缺少set_url请求参数');
		}
		if(!array_key_exists('call_back_url', $data)){
			$this->error('缺少call_back_url请求参数');
		}
		//验证请求支付平台
		$pay_type = strtolower($data['pay_type']);
		if($pay_type ==''){
			$this->error('支付方式不能为空');
		}
		$pay_channel = C('PAY_CHANNEL');
		if(empty($pay_channel)){
			//需要发短信,邮件去报警
			self::tellManager('000004');
			\Think\Log::write('支付网关pay_channel出现异常.client IP:'.get_client_ip());
			$this->error('支付网关不能为空.~.~请联系PHP@妖孽');
		}
		if(!in_array($pay_type, array_keys($pay_channel))){
			//需要发短信,邮件去报警
			self::tellManager('000005');
			\Think\Log::write('支付网关不合法.client IP:'.get_client_ip().'===pay_type:'.$pay_type);
			$this->error('支付网关配置出现异常.~请联系PHP@妖孽');
		}
		
		$goods_title = $data['goods_title'];
		if($goods_title ==''){
			$this->error('商品名称不能为空');
		}
		$goods_des = $data['goods_des'];
		$order_money = $data['order_money'];
		if(!isMoney($order_money)){
			$this->error('请输入正确的订单价钱');
		}
		
		$set_url = $data['set_url'];
		if(!checkUrl($set_url)){
			$this->error('set url参数有误,请输入www.itdaodan.com如此的域名.~谢谢合作');
		}
// 		$set_url = urlencode($set_url);
		$call_back_url = $data['call_back_url'];
		if(!checkUrl($call_back_url)){
			$this->error('call_back_url参数有误,请输入www.itdaodan.com如此的域名.~谢谢合作');
		}
// 		$client_order_no = (String)$data['client_order_no'];
// 		if($client_order_no =='' || strlen($client_order_no) !=8){
// 			$this->error('客户端订单号不能为空');
// 		}
		
		$client_order_no = (int)$data['client_order_no'];
		if($client_order_no ==0){
			$this->error('客户端订单号不能为空');
		}
		
		
		if(strlen($goods_title)>=20){
			$goods_title = msubstr($goods_title, 0,20,'utf-8',false);
		}
		
		if(strlen($goods_des)>100){
			$goods_des = msubstr($goods_title, 0,60,'utf-8',false);
		}
		
// 		$channel = C('PAY_STATIC');
// 		$channel_id = (array_key_exists($pay_type, $channel)?$channel[$pay_type]:0);
		
		try {
			$pay = new \Think\Pay($pay_type, C('payment.' . $pay_type));
			$order_no = $pay->createOrderNo();
			$vo = new \Think\Pay\PayVo();
			$vo->setBody($goods_des)//商品描述
			->setClientOrderNo($client_order_no)//客户端支付订单号
			->setFee($order_money) //支付金额
			->setOrderNo($order_no)//订单编号
			->setTitle($goods_title)//商品标题
			->setCallback($call_back_url)//设置支付完成后的后续操作接口
			->setUrl($set_url)//设置支付完成后的跳转地址
			->setAuthId($this->auth_id)
			->setChannelId($this->payTypeChannleId($pay_type))
			->setOpenId(isset($data['openid'])?$data['openid']:'')
			->setParam(array('server_order_id' => $order_no,'client_order_no'=>$client_order_no));
			$form = $pay->buildRequestForm($vo);
			\Think\Log::write('表单创建成功.client IP:'.get_client_ip().'===server_order_id:'.$order_no.'====client_order_no:'.$client_order_no);
			$this->ajaxReturn(array('status'=>1,'data'=>array('form'=>$form)));
		}catch (\Exception  $e){
			\Think\Log::write('表单创建失败.client IP:'.get_client_ip().'==info:'.$e->getMessage());
			$this->ajaxReturn(array('status'=>0,'info'=>$e->getMessage()));
		}
	}
	
	/**
	 * @todo 根据支付渠道转换成对应渠道id
	 * @param string $pay_type
	 * @return number
	 */
	private function payTypeChannleId($pay_type){
		$channel = C('PAY_STATIC');
		return (array_key_exists($pay_type, $channel)?$channel[$pay_type]:0);
	}
	
	/**
	 * @todo 退款接口
	 * //为后续退款到某个账户做准备
	 */
	private function refund(&$data){
		if(!array_key_exists('pay_type', $data)){
			$this->error('缺少支付方式~');
		}
		//验证请求支付平台
		$pay_type = strtolower($data['pay_type']);
		if($pay_type ==''){
			$this->error('支付方式不能为空');
		}
		$pay_channel = C('PAY_CHANNEL');
		if(empty($pay_channel)){
			//需要发短信,邮件去报警
			self::tellManager('000010');
			\Think\Log::write('支付网关pay_channel出现异常.client IP:'.get_client_ip());
			$this->error('支付网关不能为空.~.~请联系PHP@妖孽');
		}
		if(!in_array($pay_type, array_keys($pay_channel))){
			//需要发短信,邮件去报警
			self::tellManager('000011');
			\Think\Log::write('支付网关不合法.client IP:'.get_client_ip().'===pay_type:'.$pay_type);
			$this->error('支付网关配置出现异常.~请联系PHP@妖孽');
		}
		$out_trade_no = (string)$data['out_trade_no'];//支付网关订单号 
		$client_order_no = (int)$data['client_order_no'];//渠道订单编号
		if($client_order_no ==0){
			$this->error('客户端订单号不能为空');
		}
		if($out_trade_no =='' || strlen($out_trade_no) !=15 ){
			$this->error('支付网关订单号 不能为空');
		}
		//渠道id和支付渠道id
		$channel_id = $this->payTypeChannleId($data['pay_type']);//支付渠道id
		$map = array('out_trade_no' => $out_trade_no,'client_order_no'=>$client_order_no,'auth_id'=>$this->auth_id,'channel_id'=>$channel_id);
		$obj = M("Pay");
		$order_info = $obj->field('status')->where($map)->find();
		//订单存在并且支付成功
		if(!empty($order_info) && $order_info['status'] == 1){
			$is_update = $obj->where($map)->setField(array('update_time' => NOW_TIME, 'status' => 3));
			if( false !== $is_update){
				\Think\Log::write('退款成功:订单号'.$out_trade_no.'客户端订单号 :'.$client_order_no.'渠道:'.$this->auth_id.'支付方式:'.$pay_type);
				$this->ajaxReturn(array('status'=>1,'info'=>'退款成功'));
			}else{
				\Think\Log::write('退款失败:订单号'.$out_trade_no.'客户端订单号 :'.$client_order_no.'渠道:'.$this->auth_id.'支付方式:'.$pay_type);
				$this->ajaxReturn(array('status'=>0,'info'=>$obj->getError()));
			}
		}else{
			\Think\Log::write('退款失败:[订单不存在,或者未支付]订单号'.$out_trade_no.'客户端订单号 :'.$client_order_no.'渠道:'.$this->auth_id.'支付方式:'.$pay_type);
			$this->ajaxReturn(array('status'=>0,'info'=>'订单不存在,或者未支付'));
		}
		
	}
	
	/**
	 * @todo 查询第三方平台支付订单状态
	 * @param out_trade_no  订单号
	 * @return array
	 * <li>status (int)</li>状态 ） －1，0，1 （－1异常错误，0常规错误，1操作成功）
	 * <li>data</li>返回数据
	 * <li>info</li>提示信息
	 */
	private function getOrderQuery(&$data){
	    if(!array_key_exists('pay_type', $data)){
	        $this->error('缺少支付方式~');
	    }
	    //验证请求支付平台
	    $pay_type = strtolower($data['pay_type']);
	    if($pay_type ==''){
	        $this->error('支付方式不能为空');
	    }
	    $pay_channel = C('PAY_CHANNEL');
	    if(empty($pay_channel)){
	        //需要发短信,邮件去报警
	        self::tellManager('000020');
	        \Think\Log::write('支付网关pay_channel出现异常.client IP:'.get_client_ip());
	        $this->error('支付网关不能为空.~.~请联系PHP@妖孽');
	    }
	    if(!in_array($pay_type, array_keys($pay_channel))){
	        //需要发短信,邮件去报警
	        self::tellManager('000021');
	        \Think\Log::write('支付网关不合法.client IP:'.get_client_ip().'===pay_type:'.$pay_type);
	        $this->error('支付网关配置出现异常.~请联系PHP@妖孽');
	    }
	    $out_trade_no = (string)$data['out_trade_no'];//支付网关订单号
	    // 	    $client_order_no = (int)$data['client_order_no'];//渠道订单编号
	    // 	    if($client_order_no ==0){
	    // 	        $this->error('客户端订单号不能为空');
	        // 	    }
        if($out_trade_no =='' || strlen($out_trade_no) !=15 ){
            $this->error('支付网关订单号 不能为空');
        }
        try {
            $pay = new \Think\Pay($pay_type, C('payment.' . $pay_type));
            $vo = new \Think\Pay\PayVo();
            $vo->setOrderNo($out_trade_no);//订单编号
            $order_info = $pay->getOrderQuery($vo);
            \Think\Log::write('订单查询：'.json_encode($order_info));
            if(empty($order_info)){
                E('暂无此订单信息');
            }
            $obj = M('Pay');
            $map = array('out_trade_no' => $order_info['out_trade_no']);
            $pay_info = $obj->where($map)->find();
            if(empty($pay_info)){
                E('支付网关暂无此订单信息');
            }
            import('Common.Msj.Curl');
            $curl_obj = new \Curl();
            switch ($pay_info['status']){
                case 0 ://支付网关未收到通知
                    //如果支付成功先改支付网关，再回调第三方
                    $is_update = $obj->where($map)->save(array('status'=>1,'update_time' => NOW_TIME));
                    if(false === $is_update){
                        //修改失败～重新操作
                        $this->ajaxReturn(array('status'=>0,'info'=>'订单状态更新失败请重新操作'));
                    }
                    $order_info['attach'] = unserialize($pay_info['param'])+ array('method'=>$pay_type);
                    //如果支付网关收到通知，直接回调第三方
                    $info = $curl_obj->curl_post($pay_info['callback'],array('money' => $pay_info['money'], 'param' => $order_info['attach']));
                    $info =json_decode($info,true);
                    if(empty($info) || is_null($info)){
                        E('客户端返回数据有误～');
                    }
                    $call_back = array();
                    if($info['status']==1){
                        $call_back['status']=1;
                        $call_back['data']=$order_info;
                    }else{
                        $call_back['status']=0;
                        $call_back['info']=$info['info'];
                    }
                    $this->ajaxReturn($call_back);
                    break;
                case 1 :
                    $order_info['attach'] = unserialize($pay_info['param'])+ array('method'=>$pay_type);
                    //如果支付网关收到通知，直接回调第三方
                    $info = $curl_obj->curl_post($pay_info['callback'],array('money' => $pay_info['money'], 'param' => $order_info['attach']));
                    $info =json_decode($info,true);
                    if(empty($info) || is_null($info)){
                        E('客户端返回数据有误～');
                    }
                    $call_back = array();
                    if($info['status']==1){
                        $call_back['status']=1;
                        $call_back['data']=$order_info;
                    }else{
                        $call_back['status']=0;
                        $call_back['info']=$info['info'];
                    }
                    $this->ajaxReturn($call_back);
                    break;
                case 2 :
                default:
                    //订单退款
                    $this->ajaxReturn(array('status'=>0,'info'=>'操作有误～'));
                    break;
            }
        }catch (\Exception  $e){
            \Think\Log::write('订单查询失败.client IP:'.get_client_ip().'==info:'.$e->getMessage());
            $this->ajaxReturn(array('status'=>0,'info'=>$e->getMessage()));
        }
	}
	
	/**
	 * @todo 获取支持的支付平台
	 */
	private function getPayList(&$data){
		$pay_list = C('PAY_CHANNEL');
		if(empty($pay_list)){
			$this->ajaxReturn(array('status'=>0,'info'=>'暂无支持的支付平台'));
		}else{
			$this->ajaxReturn(array('status'=>1,'data'=>$pay_list));
		}
	}
	
	
	/**
	 * @todo 获取微信openid
	 */
	private function getOpenId(&$data){
// 		$pay = new \Think\Pay('wechatwappay', C('payment.wechatwappay'));
		$pay = new \Think\Pay\Driver\Wechatwappay(C('payment.wechatwappay'));
		$client_order_no = (int)$data['client_order_no'];
		if($client_order_no ==0){
			$this->error('客户端订单号不能为空');
		}
		
		$url = $data['url'];
		if(!checkUrl($url)){
			$this->error(' url参数有误,请输入www.itdaodan.com如此的域名.~谢谢合作');
		}
		$open_id = $pay->makeOpenId(array('auth_id'=>$this->auth_id,'client_order_no'=>$client_order_no,'url'=>$url));
		if(!$open_id){
			$this->ajaxReturn(array('status'=>0,'info'=>'暂无支持的支付平台'));
		}else{
			$this->ajaxReturn(array('status'=>1,'data'=>$open_id));
		}
		//实例化驱动  获取openid传回客户端,
	}
	
	
	/**
	 * @todo 服务器报警
	 */
	private static function tellManager($status){
		//发送短信和邮件
		D('Api/Sms','Service')->sendSms('13263512111','你好，这里是PHP@妖孽风控报警系统，目前系统有风险，请及时查看系统日志。时间:'.date('Y-m-d H:i:s').'错误码:'.$status);
	}

}
